/*
 * Copyright 2011 Helios Technologies.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.heliostech.gwt.widget.dialog.base;

import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.resources.client.ClientBundle;
import com.google.gwt.resources.client.CssResource;
import com.google.gwt.resources.client.CssResource.ImportedWithPrefix;
import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.user.client.ui.AbstractImagePrototype;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.IsWidget;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.LayoutPanel;
import com.google.gwt.user.client.ui.SimplePanel;

/**
 * @author ldesdier
 *
 */
public class BaseDialog extends DialogBox {

	public interface Resources extends ClientBundle {
		@Source("./close.png")
		ImageResource closeIcon();
		@Source(Style.DEFAULT_CSS)
		@CssResource.NotStrict
	    Style baseDialogStyle();
	}
	
	@ImportedWithPrefix("helios-BaseDialog")
	public interface Style extends CssResource {
		String DEFAULT_CSS = "com/heliostech/gwt/widget/dialog/base/BaseDialog.css";
		
		String baseDialogWidget();
		
		String mainLayout();
		
		String descriptionLayout();

		String actionLabel();

		String descriptionLabel();
		
		String error();

		String descriptionIcon();

		String buttonPanel();

		String dialogContent();
		
	}
	
	private static Resources DEFAULT_RESOURCES;
	private static Resources getDefaultResources() {
		if (DEFAULT_RESOURCES == null) {
			DEFAULT_RESOURCES = GWT.create(Resources.class);
		}
		return DEFAULT_RESOURCES;
	}
	
	private final Style style;
	private final LayoutPanel layout;
	private LayoutPanel descriptionPanel;
	private Label actionLabel;
	private Label descriptionLabel;
	private String defaultLabel;
	private Image iconImg;
	private final SimplePanel content;
	private final FlowPanel buttonPanel;
	
	/**
	 * 
	 */
	public BaseDialog(String title) {
		this(title, false);
	}
	
	public BaseDialog(String title, boolean showDescription) {
		this(title, false, false);
	}
	
	public BaseDialog(String title, boolean showDescription, boolean modal) {
		this(title, showDescription, modal, getDefaultResources());
	}
	
	/**
	 * @param autoHide
	 * @param modal
	 */
	public BaseDialog(String title, boolean showDescription, boolean modal, BaseDialog.Resources resources) {
		super(false, modal);
		setGlassEnabled(modal);
		this.style = resources.baseDialogStyle();
		this.style.ensureInjected();
		addStyleName(style.baseDialogWidget());
		// Build the dialog title bar with the title and the close icon.
		setDialogTitle(title);
		initNativeFunction(this);
		
		// The main dialog layout
		layout = new LayoutPanel();
		layout.addStyleName(style.mainLayout());
		
		// If specified, show the description panel
		if(showDescription) {
			descriptionPanel = new LayoutPanel();
			descriptionPanel.addStyleName(style.descriptionLayout());
			final SimplePanel dContainer = new SimplePanel();
			dContainer.setWidget(descriptionPanel);
			layout.add(dContainer);
			layout.setWidgetTopHeight(dContainer, 0, Unit.PX, 4, Unit.EM);
			layout.setWidgetLeftRight(dContainer, 0, Unit.PX, 0, Unit.PX);
		}
			
		// The dialog content wrapper
		content = new SimplePanel();
		content.addStyleName(style.dialogContent());
		layout.add(content);
		layout.setWidgetTopBottom(content, 4, Unit.EM, 3, Unit.EM);
		layout.setWidgetLeftRight(content, 0, Unit.PX, 0, Unit.PX);
		
		// The bottom panel set to receive the buttons.
		buttonPanel = new FlowPanel();
		buttonPanel.addStyleName(style.buttonPanel());
		final SimplePanel bContainer = new SimplePanel();
		bContainer.setWidget(buttonPanel);
		layout.add(bContainer);
		layout.setWidgetBottomHeight(bContainer, 0, Unit.PX, 3, Unit.EM);
		layout.setWidgetLeftRight(bContainer, 0, Unit.PX, 0, Unit.PX);
		
		this.setWidget(layout);
	}
	
	public void setDialogTitle(String title) {
		SafeHtmlBuilder sb = new SafeHtmlBuilder();
		ImageResource ci = ((Resources)GWT.create(Resources.class)).closeIcon();
		sb.appendHtmlConstant("<table><tr><td width='100%'>").appendEscaped(title).appendHtmlConstant("</td><td><a onclick='_closeDialog_()'>").
			appendHtmlConstant(AbstractImagePrototype.create(ci).getHTML()).appendHtmlConstant("</a></td><tr></table>");
		setHTML(sb.toSafeHtml());
	}
	
	/**
	 * Initializes native functions.
	 * @param impl
	 */
	public native void initNativeFunction(BaseDialog impl) /*-{
		$wnd._closeDialog_ = function() {
			impl.@com.heliostech.gwt.widget.dialog.base.BaseDialog::dismiss()();
		};
	}-*/;

	/**
	 * Close this dialog after having cleared any error message.
	 * implementation for reuse.
	 */
	public void dismiss() {
		clearError();
		cleanup();
		super.hide();
	}
	
	/**
	 * Cleanup the dialog for reuse. This is a extension point. Ovveride this method to execute your
	 * own logic for your dialog implementation cleanup.
	 */
	public void cleanup() {
		
	}
	
	/**
	 * Sets the specified widget as the dialog main content
	 * @param w
	 */
	public void setContent(IsWidget w) {
		this.content.setWidget(w);
	}
	
	/**
	 * Set the dialog description panel content.
	 * @param action The action to be performed, displayed as the top of the description
	 * @param description The description of the action to be performed
	 */
	public void setDialogDescription(String action, String description) {
		if(descriptionPanel != null) {
			defaultLabel = description;
			if(actionLabel == null) {
				actionLabel = new Label(action);
				actionLabel.addStyleName(style.actionLabel());
				descriptionPanel.add(actionLabel);
				descriptionPanel.setWidgetTopHeight(actionLabel, 0, Unit.PX, 1.5, Unit.EM);
				descriptionPanel.setWidgetLeftRight(actionLabel, 0, Unit.PX, 0, Unit.PX);
			} else {
				actionLabel.setText(action);
			}
			ensureDescriptionLabel();
			descriptionLabel.setText(description);
		} else {
			throw new UnsupportedOperationException("BaseDialog should be instantiated with showDescription parameter set to true.");  
		}
	}
	
	/**
	 * Ensure that the description label is existing.
	 */
	private void ensureDescriptionLabel() {
		if(descriptionLabel == null) {
			descriptionLabel = new Label();
			descriptionLabel.addStyleName(style.descriptionLabel());
			descriptionPanel.add(descriptionLabel);
			descriptionPanel.setWidgetTopHeight(descriptionLabel, 1.5, Unit.EM, 2, Unit.EM);
			descriptionPanel.setWidgetLeftRight(descriptionLabel, 0, Unit.PX, 0, Unit.PX);
		}
	}
	
	/**
	 * Set the dialog description panel content.
	 * @param action The action to be performed, displayed as the top of the description
	 * @param description The description of the action to be performed
	 * @param icon An icon identifying this action.
	 */
	public void setDialogDescription(String action, String description, ImageResource icon) {
		this.setDialogDescription(action, description);
		if(iconImg == null) {
			iconImg = new Image(icon);
			// We add 4 for a padding around the img
			int w = iconImg.getWidth();
			int h = iconImg.getHeight();
			iconImg.addStyleName(style.descriptionIcon());
			descriptionPanel.add(iconImg);
			descriptionPanel.setWidgetTopHeight(iconImg, 4, Unit.PX, h, Unit.PX);
			descriptionPanel.setWidgetRightWidth(iconImg, 4, Unit.PX, w, Unit.PX);
			descriptionPanel.setWidgetLeftRight(actionLabel, 0, Unit.PX, w+4, Unit.PX);
			descriptionPanel.setWidgetLeftRight(descriptionLabel, 0, Unit.PX, w+4, Unit.PX);
		} else {
			iconImg.setResource(icon);
		}
	}
	
	/**
	 * Add the specified button to the button bar located at the bottom.
	 * @param b
	 */
	public void addButton(Button b) {
		buttonPanel.add(b);
	}
	
	/**
	 * Remove the button at the specified index from the button bar located at the bottom.
	 * @param index
	 * @return
	 * @see com.google.gwt.user.client.ui.ComplexPanel#remove(int)
	 */
	public boolean removeButton(int index) {
		return buttonPanel.remove(index);
	}

	/**
	 * Remove the specified button from the button bar located at the bottom.
	 * @param w
	 * @return
	 * @see com.google.gwt.user.client.ui.LayoutPanel#remove(com.google.gwt.user.client.ui.Widget)
	 */
	public boolean removeButton(Button b) {
		return buttonPanel.remove(b);
	}

	/* (non-Javadoc)
	 * @see com.google.gwt.user.client.ui.UIObject#setSize(java.lang.String, java.lang.String)
	 */
	@Override
	public void setSize(String width, String height) {
		layout.setSize(width, height);
	}
	
	/**
	 * Show the provided error message in place of the dialog description.
	 * @param msg The error message to be shown.
	 */
	public void showError(String msg) {
		if(descriptionPanel != null) {
			ensureDescriptionLabel();
			descriptionLabel.setText(msg);
			descriptionLabel.addStyleName(style.error());
		} else {
			throw new UnsupportedOperationException("BaseDialog should be instantiated with showDescription parameter set to true.");  
		}
	}
	
	/**
	 * Clear any error message and reset the dialog description to its default value.
	 */
	public void clearError() {
		if(descriptionPanel != null && descriptionLabel != null) {
			descriptionLabel.setText(defaultLabel);
			descriptionLabel.removeStyleName(style.error());
		}
	}

}
